package cn.android666.timbertree

import android.annotation.SuppressLint
import android.content.Context
import android.text.format.DateFormat
import android.util.Log
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import timber.log.Timber
import java.io.File
import java.lang.Exception

/**
 * 使用Logback实现的文件树，只保存INFO级别或更高级别的日志。
 * INFO 级别以及更高级别的日志保存到应用目录的logs目录中
 * ERROR级别以及更高级别的日志同时还保存到应用目录的bugs目录中
 */
class FileTree(context: Context) : Timber.DebugTree() {


    // getExternalFilesDir方法会自动创建外部存储的应用目录，只能通过此函数调用自动创建，不能手动创建。
    // 也就是说/storage/emulated/0/Android/data/com.example/files/，这个私有专用目录不能手动创建，
    // 需要使用getExternalFilesDir来创建，当files目录创建了之后，才可以手动在里面创建其它目录或文件。
    // Logback创建目录时没有调用此方法，所以在没有生成应用目录时，Logback创建目录会失败。
    // 必须在调用Logback的代码之前先调用这个函数，以生成应用目录，这样Logback在此目录的基础上再创建其他目录时才能创建成功！
    val logFilesDir: File = context.getExternalFilesDir("logs")!!
    val bugFilesDir: File = context.getExternalFilesDir("bugs")!!
    val logback: Logger

    init {
        fixBugFileProblem()
        // 必须在fixBugFileProblem()方法调用之后再初始化Logback
        logback = LoggerFactory.getLogger(FileTree::class.java)
    }

    override fun isLoggable(tag: String?, priority: Int): Boolean {
        return priority >= Log.INFO // info或更高的级别的日志才应该被记录到文件中
    }

    override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
        val msg = "$tag：[${Thread.currentThread().name}] $message" // message中已经包含异常的信息，所以这里用不到参数t
        when (priority) {
            Log.INFO -> logback.info(msg)
            Log.WARN -> logback.warn(msg)
            Log.ERROR, Log.ASSERT -> logback.error(msg)
        }
    }

    /**
     * 修复Bug文件存在的问题：
     * 因为Logback需要在第二天产生新Bug时，才会把第一天产生的bug.log重命名为bug_2021_02_28.log，
     * 如果第二天不产生Bug就不好了，所以这里手动实现重命名。
     * 注：当Logback初始化的时候，会自动创建出bug.log文件，如果你此时删除此文件，即使再手动创建一个，则Logback也不会写日志到文件里面了。
     * 所以，我们需要在Logback初始化之前修改bug.log为具体日期的文件，然后再初始化Logback，这样Logback就会再自动创建一个bug.log文件，这样写日志就不会有问题。
     * 普通日志文件就不用管，因为应用一启动肯定就会产生一些普通日志的，但是bug只是偶尔才会产生。
     */
    @SuppressLint("LogNotTimber")
    private fun fixBugFileProblem() {
        val bugFile = File(bugFilesDir, "/bug.log")
        val format = "yyyy_MM_dd"
        val currentDate = DateFormat.format(format, System.currentTimeMillis())
        val fileLastModifiedDate = DateFormat.format(format, bugFile.lastModified())
        val exists = bugFile.exists()

        if (!exists || currentDate == fileLastModifiedDate) {
            // 如果bug.log文件不存在，或者如果存在且修改日期和当前日期相同，则不需要做任何处理。
            return
        }

        // bug.log文件存在，且文件的最后修改日期和当天不相等。
        // 如果不是当天的文件，需要我们手动重命名。
        if (bugFile.length() == 0L) {
            // 如果bug文件是空的，则直接删除让其重新创建一个新的就好了，就不需要手动重命名了。
            bugFile.delete()
        } else {
            val tag = FileTree::class.java.simpleName
            val renameFile = File(bugFile.parentFile, "bug_$fileLastModifiedDate.log")
            var renameResult = false
            try {
                renameResult = bugFile.renameTo(renameFile)
            } catch (e: Exception) {
                Log.i(tag, "重命名Bug文件时出现异常", e)
            }
            if (renameResult) {
                Log.i(tag, "重命名Bug文件成功：${bugFile.name} -> ${renameFile.name}")
            } else {
                Log.i(tag, "重命名Bug文件失败")
            }
        }
    }

}